//
//  JKSerialPort.m
//  Calibration
//
//  Created by ViKing Lai on 2018/1/24.
//  Copyright © 2018年 ViKing Lai. All rights reserved.
//

#import "JKSerialPort.h"
#import <IOKit/serial/IOSerialKeys.h>
#import "SerialPort.h"
typedef void(^whileBlock)();

static NSMutableArray *mPortPaths;

@implementation JKSerialPort

@synthesize devicePath = mDevicePath;

-(instancetype)init
{
    return [self initWithDevicePath:@""];

}

-(instancetype)initWithDevicePath:(NSString *)devPath
{
    _globalBuffer = [[NSMutableString alloc] init];

    return [self initWithDevicePath:devPath andBaudRate:JKBR_9600];
}

-(instancetype)initWithDevicePath:(NSString *)devPath andBaudRate:(JKBaudRate)baudRate
{
    return [self initWithDevicePath:devPath andBaudRate:baudRate andDataBit:JKDB_8 andStopBit:JKSB_ONE andParity:JKPar_NONE];
}

-(instancetype)initWithDevicePath:(NSString *)devPath andBaudRate:(JKBaudRate)baudRate andDataBit:(JKDataBit)dataBit andStopBit:(JKStopBit)stopBit andParity:(JKParity)parity
{
    self = [super init];
    
    if(self)
    {
        mFileDesriptor = -1;
        
        self.devicePath = devPath;
        self.baudrate = baudRate;
        self.dataBit = dataBit;
        self.stopBit = stopBit;
        self.parity = parity;
        
        memset(mBuffer, 0, SERIALPORT_MAX_BUFFER_SIZE);
    }
    
    return self;
}

-(void)setBaudrate:(JKBaudRate)baudrate
{
    if(mFileDesriptor != -1)
    {
        struct termios ts;
        tcgetattr(mFileDesriptor, &ts);
        ts.c_ispeed = baudrate;
        ts.c_ospeed = baudrate;
        tcsetattr(mFileDesriptor, TCSANOW, &ts);
    }
    
    if(baudrate != mBaudRate)
    {
        mBaudRate = baudrate;
    }
}

-(JKBaudRate)baudrate
{
    return mBaudRate;
}

-(void)setDataBit:(JKDataBit)dataBit
{
    if(mFileDesriptor != -1)
    {
        struct termios ts;
        tcgetattr(mFileDesriptor, &ts);
        
        if(dataBit == JKDB_8)
        {
            ts.c_cflag &= ~ISTRIP;
        }
        else
        {
            ts.c_cflag |= ISTRIP;
        }
        
        ts.c_cflag &= ~CSIZE;   //目标位 置0, 然后执行或操作，则目标位的值取决于与之相或的位的值，此处即dataBit中对应位的值
        ts.c_cflag |= dataBit;
        
        tcsetattr(mFileDesriptor, TCSANOW, &ts);
    }
    
    if(dataBit != mDataBit)
    {
        mDataBit = dataBit;
    }
}

-(JKDataBit)dataBit
{
    return mDataBit;
}

-(void)setStopBit:(JKStopBit)stopBit
{
    if(mFileDesriptor != -1)
    {
        struct termios ts;
        tcgetattr(mFileDesriptor, &ts);
        
        ts.c_cflag &= ~CSTOPB;  //目标位置0
        ts.c_cflag |= stopBit;
        
        tcsetattr(mFileDesriptor, TCSANOW, &ts);
    }
    
    if(stopBit == mStopBit)
    {
        mStopBit = stopBit;
    }
}

-(JKStopBit)stopBit
{
    return mStopBit;
}

-(void)setParity:(JKParity)parity
{
    if(mFileDesriptor != -1)
    {
        struct termios ts;
        tcgetattr(mFileDesriptor, &ts);
        
        ts.c_cflag &= ~JKPARITY_MARK;   //目标位置0
        ts.c_cflag |= parity;
        
        tcsetattr(mFileDesriptor, TCSANOW, &ts);
    }
    
    if(parity != mParity)
    {
        mParity = parity;
    }
}

-(JKParity)parity
{
    return mParity;
}

-(void)setFlowControl:(JKFlowControl)flowControl
{
    if(mFileDesriptor != -1)
    {
        struct termios ts;
        tcgetattr(mFileDesriptor, &ts);
        
        switch(flowControl)
        {
            case JKFC_HARDWARE:
                ts.c_iflag &= ~(IXON | IXOFF);
                ts.c_cflag |= CRTSCTS;
                break;
            case JKFC_SOFTWARE:
                ts.c_iflag |= (IXON | IXOFF);
                ts.c_cflag &= ~CRTSCTS;
            default:
                ts.c_iflag &= ~(IXON | IXOFF);
                ts.c_cflag &= ~CRTSCTS;
        }
        
        ts.c_cc[VSTART] = 0x11;
        ts.c_cc[VSTOP] = 0x13;
        
        tcsetattr(mFileDesriptor, TCSANOW, &ts);
    }
    
    if(flowControl != mFlowControl)
    {
        mFlowControl = flowControl;
    }
}

-(JKFlowControl)flowControl
{
    return mFlowControl;
}

-(BOOL)opened
{
    if(mFileDesriptor != -1)
    {
        return YES;
    }
    else
    {
        return NO;
    }
}


-(BOOL)open
{
    if([mDevicePath isEqualToString:@""])
    {
        return NO;
    }
    else
    {
        return [self openWithDevicePath:mDevicePath andBaudRate:mBaudRate andDataBit:mDataBit andStopBit:mStopBit andParity:mParity];
    }
}

-(BOOL)openWithDevicePath:(NSString *)devPath
{
    return [self openWithDevicePath:devPath andBaudRate:mBaudRate andDataBit:mDataBit andStopBit:mStopBit andParity:mParity];
}

-(BOOL)openWithDevicePath:(NSString *)devPath andBaudRate:(JKBaudRate)baudRate andDataBit:(JKDataBit)dataBit andStopBit:(JKStopBit)stopBit andParity:(JKParity)parity
{
    if(![devPath isEqualToString:mDevicePath])
    {
        mDevicePath = devPath;
    }
    
    const char *bsdPath = [mDevicePath fileSystemRepresentation];
    int fFlags = O_RDWR | O_NOCTTY;
    mFileDesriptor = open(bsdPath, fFlags);
    if(mFileDesriptor == -1)
    {
        return NO;
    }
    else
    {
        //user non-block mode while configure serial port
        fFlags = fcntl(mFileDesriptor, F_GETFL, 0);
        if(-1 == fcntl(mFileDesriptor, F_SETFL, fFlags | O_NONBLOCK))
        {
            [self close];
            return NO;
        }
        //flush all io data;
        if(-1 == tcflush(mFileDesriptor, TCIOFLUSH))
        {
            [self close];
            return NO;
        }
        [self setBaudrate:mBaudRate];
        [self setDataBit:mDataBit];
        [self setStopBit:mStopBit];
        [self setParity:mParity];
        
        //resume block mode for communication next
        fFlags = fcntl(mFileDesriptor, F_GETFL, 0);
        if(-1 == fcntl(mFileDesriptor, F_SETFL, fFlags &= ~O_NONBLOCK))
        {
            [self close];
            return NO;
        }
        
    }
    
    return YES;
}

-(void)close
{
    if(self.opened)
    {
        close(mFileDesriptor);
        mFileDesriptor = -1;
    }
}

-(int)dataSizeInBuffer
{
    int size;
    if(0 != ioctl(mFileDesriptor, FIONREAD, (char *)&size))
    {
        size = -1;
    }
    
    return size;
}






-(NSString*) ReadExisting
{
    //	NSAutoreleasePool *pool =[[NSAutoreleasePool alloc] init];
    NSMutableString* existing = [[NSMutableString alloc] init];
    [existing setString:@""];
    
    while ([self GetNumOfDataInBuffer] > 0) {
        [self ReadBuffer];
    }
    if (_globalBuffer != nil)
    {
        [existing appendString:_globalBuffer];
    }
    [self Set_globalBuffer:@""];
    //	[pool release];
    
    return existing;
}

-(NSString*)ReadBuffer
{
    //  NSAutoreleasePool *pool =[[NSAutoreleasePool alloc] init];
    char chr_to_str[SERIALPORT_MAX_BUFFER_SIZE] = "";
    size_t num = 0;
    
    if( [self GetNumOfDataInBuffer] > 0 )
    {
        num = read(mFileDesriptor,&chr_to_str,SERIALPORT_MAX_BUFFER_SIZE);
        
        chr_to_str[num + 1] = '\0';
        
        for(int i = 0;i < num;i++)
        {
            if(chr_to_str[i] == '\0')
            {
                chr_to_str[i] = ' ';
            }
        }
        
        NSString* readBuffer = [[NSString alloc] initWithCString:chr_to_str encoding:NSUTF8StringEncoding];
        
        if (readBuffer != nil)
        {
            if([_globalBuffer length] > 0)
            {
                [_globalBuffer appendString:readBuffer];
            }
            else
            {
                [_globalBuffer setString:readBuffer];
            }
        }
        
        
        //		[readBuffer autorelease];
    }
    
    //	[pool release];
    
    return _globalBuffer;
}







-(NSString *)readExsiting
{
    return [self readExsitingWithEncoding:NSUTF8StringEncoding];
}

-(NSString *)readExsitingWithEncoding:(NSStringEncoding)encoding
{
    int curCount;
    if((curCount = [self readBuffer]) <= 0)
    {
        return 0;
    }
    NSData*tempdata =[[NSData alloc]initWithBytes:mBuffer length:curCount];
    NSString *strResult
    = [[NSString alloc] initWithData: [[NSData alloc]initWithBytes:mBuffer length:curCount] encoding:encoding];
    
    return strResult;
}

-(int) GetNumOfDataInBuffer
{
    int result = 0;
    int err = ioctl(mFileDesriptor, FIONREAD, (char *)&result);
    
    if (err != 0)
    {
        result = -1;
    }
    
    return result;
}
-(void)Set_globalBuffer:(NSString*)buffer
{
    [_globalBuffer setString:buffer];
    //	[_globalBuffer release];
    //	[buffer retain];
    //	_globalBuffer = [NSMutableString stringWithString:buffer];
}


-(NSString *)readLine
{
    return [self readLineWithEncoding:NSUTF8StringEncoding];
}

-(NSString *)readLineWithEncoding:(NSStringEncoding)encoding
{
    return [self readToString:@"\n" withEncoding:encoding];
}

-(NSString *)readToString:(NSString *)endStr
{
    return [self readToString:endStr withEncoding:NSUTF8StringEncoding];
}

-(NSString *)readToString:(NSString *)endStr withEncoding:(NSStringEncoding)encoding
{
    int curCount;
    if((curCount = [self readBuffer]) <= 0)
    {
        return 0;
    }
    
    NSString *str = [[NSString alloc] initWithData:[[NSData alloc]initWithBytes:mBuffer length:curCount] encoding:encoding];
    NSRange range = [str rangeOfString:endStr];
    
    if(range.length <= 0)
    {
        return nil;
    }
    
    return [[str substringToIndex:range.location + range.length]
            stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceAndNewlineCharacterSet]];
}

-(NSString *)readToChar:(char)c
{
    return [self readToChar:c withEncoding:NSUTF8StringEncoding];
}

-(NSString *)readToChar:(char)c withEncoding:(NSStringEncoding)encoding
{
    return [self readToString:[[NSString alloc] initWithCharacters:(const unichar*)&c length:1] withEncoding:encoding];
}

-(int)readBytesToArray:(Byte *)bytes withCount:(int)count
{
    int curCount;
    if((curCount = [self readBuffer]) <= 0)
    {
        return 0;
    }
    
    int countReaded = (curCount >= count) ? count : curCount;
    for(int i=0; i<countReaded; i++)
    {
        bytes[i] = mBuffer[i];
    }
    
    return countReaded;
}

-(int)readNewestBytesToArray:(Byte *)bytes withCount:(int)count
{
    int curCount;
    if((curCount = [self readBuffer]) <= 0)
    {
        return 0;
    }
    
    int countReaded = (curCount >= count) ? count : curCount;
    for(int i=countReaded - 1; i>=0; i--)
    {
        bytes[i] = mBuffer[curCount - countReaded + i];
    }
    
    return countReaded;
}

-(int)readBuffer
{
    if(!self.opened)
    {
        return 0;
    }
    
    char tmpBuffer[SERIALPORT_MAX_BUFFER_SIZE] = "";
    int curCount = 0;
    
    while([self dataSizeInBuffer] > 0)
    {
        ssize_t tmpCount = read(mFileDesriptor, tmpBuffer, SERIALPORT_MAX_BUFFER_SIZE);
        
        for(int i = 0; (i<tmpCount) && (curCount < SERIALPORT_MAX_BUFFER_SIZE); i++, curCount++)
        {
            mBuffer[curCount] = tmpBuffer[i];
        }
    }
    
    return curCount;
}



-(void)writeBytes:(Byte *)bytes ofCount:(unsigned long)count
{
    if(self.opened)
    {
        write(mFileDesriptor, bytes, count);
    }
}

-(void)writeString:(NSString *)strDataToSend
{
    [self writeString:strDataToSend withEncoding:NSUTF8StringEncoding];
}

-(void)writeString:(NSString *)strDataToSend withEncoding:(NSStringEncoding)encoding
{
    NSData *data = [strDataToSend dataUsingEncoding:encoding];
    [self writeData:data];
}

-(void)writeData:(NSData *)data
{
    if(self.opened)
    {
        Byte *bytes = (Byte *)[data bytes];
        NSUInteger count = [data length];
        write(mFileDesriptor, bytes, count);
    }
}
//-(NSString*)SerialPort:(SerialPort*)portHandle WriteLineAndRead_Cmd:(NSString*)commond DelayTime:(int)delaytime TimeOut:(int)timeout readTo:(NSString*)readto;
//{
//    //    [portHandle ReadExisting];
//    //    [NSThread sleepForTimeInterval:0.02];
//    [portHandle ReadExisting];
//    [NSThread sleepForTimeInterval:0.01];
//    [portHandle WriteLine:commond];
//    [NSThread sleepForTimeInterval:0.01];
//    
//    [NSThread sleepForTimeInterval:delaytime/1000.0];
//    NSString* tempData = @"";
//    if (readto != nil && ![readto isEqualToString:@""]) {
//        tempData = [portHandle ReadTo:readto Timeout:timeout ReadInterval:100];//debug
//    }
//    if([tempData isEqualToString:@""])
//    {
//        tempData =[portHandle ReadExisting];
//    }
//    
//    return tempData;
//}

-(NSData *)writeAndReadBytes:(Byte *)bytes ofCount:(int)count withInterval:(double)intervalInSeconds
{
    [self writeBytes:bytes ofCount:count];
    
    [NSThread sleepForTimeInterval:intervalInSeconds];
    
    int validBufSize = [self readBuffer];
    NSData *rData = [[NSData alloc] initWithBytes:mBuffer length:validBufSize];
    
    return rData;
}

-(NSString *)writeAndReadString:(NSString *)strDataToSend withInterval:(double)intervalInSeconds
{
    return [self writeAndReadString:strDataToSend withEncoding:NSUTF8StringEncoding withInterval:intervalInSeconds];
}

-(NSString *)writeAndReadString:(NSString *)strDataToSend withEncoding:(NSStringEncoding)encoding withInterval:(double)intervalInSeconds
{
    [self writeString:strDataToSend withEncoding:encoding];
    
    [NSThread sleepForTimeInterval:intervalInSeconds];
    
    return [self readExsitingWithEncoding:encoding];
}

static dispatch_once_t once_Token;
+(NSArray *)portPaths
{
    dispatch_once(&once_Token, ^()
                  {
                      mPortPaths = [[NSMutableArray alloc]init];
                  });
    [mPortPaths removeAllObjects];
    
    CFMutableDictionaryRef dicToMatch = IOServiceMatching(kIOSerialBSDServiceValue);
    if(dicToMatch != NULL)
    {
        CFDictionarySetValue(dicToMatch, CFSTR(kIOSerialBSDTypeKey), CFSTR(kIOSerialBSDAllTypes));
        io_iterator_t portIterator;
        kern_return_t rValue = IOServiceGetMatchingServices(kIOMasterPortDefault, dicToMatch, &portIterator);
        if(rValue == KERN_SUCCESS)
        {
            io_object_t portEntry;
            while((portEntry = IOIteratorNext(portIterator)) != 0)
            {
                CFStringRef bsdPath = (CFStringRef)IORegistryEntryCreateCFProperty(portEntry, CFSTR(kIOCalloutDeviceKey), kCFAllocatorDefault, 0);
                if(bsdPath)
                {
                    NSString *path = [[NSString alloc]initWithString:(__bridge NSString*)bsdPath];
                    [mPortPaths addObject:path];
                    
                    CFRelease(bsdPath);
                }
            }
        }
    }
    
    return mPortPaths;
}

@end
